package cn.tedu.charging.order.service.impl;

import cn.tedu.charging.common.pojo.JsonResult;
import cn.tedu.charging.common.pojo.param.UpdateGunStatusParam;
import cn.tedu.charging.common.pojo.vo.UserDetailVO;
import cn.tedu.charging.common.utils.JsonUtils;
import cn.tedu.charging.order.common.GunStatus;
import cn.tedu.charging.order.common.MqttConstants;
import cn.tedu.charging.order.feign.DeviceFeignClient;
import cn.tedu.charging.order.feign.UserFeignClient;
import cn.tedu.charging.order.mqtt.MqttProducer;
import cn.tedu.charging.order.pojo.dto.ChargingCommandDto;
import cn.tedu.charging.order.pojo.param.OrderAddParam;
import cn.tedu.charging.order.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

/**
 * 订单业务实现类
 */
@Slf4j
@Service
public class OrderServiceImlp implements OrderService {
    /**
     * 创建订单
     *
     * @param orderAddParam
     * @return 订单服务要想使用用户服务
     * 必须要通过接口(调用用户服务的用户详情接口)来进行通信
     * <p>
     * 不能通过 订单服务 依赖用户 使用用户服务的UserService 来获取用户详情信息
     * @Autowired UserService userService;
     * <p>
     * 我们要在订单服务 通过 java代码 调用 用户服务 的 详情接口 获取 用户详情
     * 关于这段代码如何快速出现  键盘输入 /**回车即可
     */
    @Override
    public String createOrder(OrderAddParam orderAddParam) {

        log.debug("创建订单，生成订单号：{}");
        String orderNo = generateOrderNo();
        log.debug("创建订单，生成订单号：{}", orderNo);

        Integer userId = orderAddParam.getUserId();
        log.debug("创建订单，调用用户服务获取用户信息，入参：｛｝",userId);

        //一个通过用户id获取用户详细信息的方法:getUserDetailByUserId
        //UserDetailVO userDetailVO = getUserDetailByUserId(userId);
        UserDetailVO userDetailVO = getUserDetailByUserIdByFeignClient(userId);
        log.debug("调用用户服务获取用户信息,入参:{},出参:{}",userId,userDetailVO);
        if (userDetailVO == null) {
            log.error("创建订单，用户不存在，入参:{}", userId);
            //抛出异常     异常信息
            throw new RuntimeException("用户不存在");
        }
        Integer gunId=orderAddParam.getGunId();
        log.debug("创建订单，调用设备服务修改抢的状态为已使用，入参:{}",gunId);
        Boolean result = updateGunStatus(gunId);
        log.debug("创建订单，调用设备服务修改抢的状态为已使用，入参:{},出参:{}",gunId,result);

        //定义一个桩id
        Integer pileId = orderAddParam.getPileId();
        log.debug("给设备发送开始充电的指令，入参-订单号:{},桩id:{},枪id:{}",orderNo,pileId,gunId);
        Boolean startChargingResult = startCharging(orderNo, pileId, gunId);
        log.debug("给设备发送开始充电的指令，入参-订单号:{},桩id:{},枪id:{},出参:{}",orderNo,pileId,gunId,startChargingResult);

        log.debug("发送消息");
        sendMessage();

        return orderNo;

    }

    /**
     * 发送消息
     */
    private void sendMessage() {
    }

    @Autowired
    private MqttProducer mqttProducer;
    /*
     * 给设备发送开始充电的指令
     */
    private Boolean startCharging(String orderNo,Integer pileId,Integer gunId) {   //pileId 用户扫描到的充电桩id
        //1>定义Topic
        //区分消息要发送到不同的设备 不同的充电桩 用不同的topic区分  /topic/charging/start/桩id
        //户扫描了那个设备就给哪个设备发消息，根据用户扫描的设备id 来拼接topic    添加 桩id： Integer pileId
        String topic = MqttConstants.TOPIC_CHARGING_START + pileId;
        //2>定义消息
        //明确业务目标 订单服务 告诉 设备 要做的事情
        //订单服务告诉 充电桩 pileId  用哪个枪 gunId  给哪个订单 orderNo  干什么事情 START_CHARGING_COMMAND 开始充电
        ChargingCommandDto chargingCommandDto = new ChargingCommandDto();
        chargingCommandDto.setOrderNo(orderNo);
        chargingCommandDto.setPileId(pileId);
        //订单服务给设备发送消息的时候 需要携带着枪id，表示具体用哪个枪开始充电    添加 抢id ： Integer gunId
        chargingCommandDto.setGunId(gunId);
        //开始充电指令     MqttConstants常量类中，添加一个常量  START_CHARGING_COMMAND
        chargingCommandDto.setCommand(MqttConstants.START_CHARGING_COMMAND);
        //我们发的消息是一个字符串，显然不符合要求，无法传输 需要转换
        //3>转换消息
        //把 chargingCommandDto 转换成 json 字符串  需要有一个转换器  JsonUtils    公共类，创建到charging-common 中
       String json = JsonUtils.toJson(chargingCommandDto);
       //4>发送消息
       mqttProducer.sendMessage(topic,json);
       return true;

    }
    @Autowired
    private DeviceFeignClient deviceFeignClient;

    /**
     * 更新抢的状态为已使用
     * @param gunId
     * @return
     */
    private Boolean updateGunStatus(Integer gunId) {
        UpdateGunStatusParam updateGunStatusParam = new UpdateGunStatusParam();
        updateGunStatusParam.setGunId(gunId);
        updateGunStatusParam.setGunStatus(GunStatus.CHARGING);  //.CHARGING  2 充电中
        JsonResult<Boolean> booleanJsonResult = deviceFeignClient.updateGunStatus(updateGunStatusParam);
        if (booleanJsonResult != null) { //判断是否为空
            return booleanJsonResult.getData(); //有数据 返回数据  更新成功
        }
        return false; //没有数据 返回false 更新失败
    }

    /**
     * 生成唯一的 订单号
     */
    private String generateOrderNo() {
        return "666";
    }

    @Autowired
    private UserFeignClient userFeignClient;

    /**
     * 调用用户服务获取用户信息
     * userFeignClient
     */
    private UserDetailVO getUserDetailByUserIdByFeignClient(Integer userId) {
        JsonResult<UserDetailVO> userDetailVOJsonResult = userFeignClient.userDetil(userId);
        if(userDetailVOJsonResult!=null){
            return userDetailVOJsonResult.getData();
        }
        return null;
    }


    @Autowired
    private RestTemplate restTemplate;
    /**
     * 调用用户服务获取用户信息
     * restTemplate.exchange
     */
    public UserDetailVO getUserDetailByUserId(Integer userId) {
        // 直接拼接方式
        // String url = "http://localhost:8080/user/detail/" + userId; //入参拼接到接口地址
        //占位符
        //String url = "http://localhost:8080/user/detail/{userId}";
        //使用服务名称代替IP地址（charging-user 需在Nacos注册）
        String url = "http://charging-user/user/detail/{userId}";
        /**
         * 返回类型 JsonResult<UserDetailVO> 数据是要
         * 为什么用 ParameterizedTypeReference？
         * Java 泛型在运行时会被擦除，ParameterizedTypeReference 通过匿名子类保留泛型信息，确保正确反序列化。
         */
        ParameterizedTypeReference<JsonResult<UserDetailVO>> reptypeReference  = new ParameterizedTypeReference<>(){};

        /**
         * public <T> ResponseEntity<T> exchange(
         *     String url,                     // 请求URL（可含占位符）
         *     HttpMethod method,              // HTTP方法（GET/POST等）
         *     HttpEntity<?> requestEntity,    // 请求实体（含头和体）
         *     ParameterizedTypeReference<T> responseType, // 响应类型（支持泛型）
         *     Object... uriVariables          // URL占位符参数
         * ) throws RestClientException
         *
         * 通过 exchange() 方法，可以实现高度定制的 HTTP 通信
         * 调用方式 exchange （交换）调用接口               1 接口地址 url,2 调用方式 GET 3 入参 4  返回类型 reptypeReference  5 占位符  入参 userId
         */
        ResponseEntity<JsonResult<UserDetailVO>> exchange = restTemplate.exchange(url, HttpMethod.GET, null, reptypeReference, userId);
        JsonResult<UserDetailVO> body = exchange.getBody();
        UserDetailVO data = body.getData();
        //JsonResult 统一返回结果的 响应数据
        return data;
    }

}